package com.licode.codekit.buildprj;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.Gray;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBScrollPane;
import com.licode.codekit.db.AssembledDb;
import com.licode.codekit.db.LiSysConfig;
import com.licode.codekit.db.Table;
import com.licode.codekit.modal.CreateModal;
import com.licode.codekit.utils.DbDriverDlUtil;
import com.licode.codekit.utils.LiConfig;
import com.licode.codekit.utils.PomFile;
import com.licode.codekit.utils.UIBuilderUtil;

import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;

/**
 * @author licode
 */
public class GenerateCodeUiBuilder {
    public static int DEFAULT_WID = 460;
    private final int LABEL_ALIGN = SwingConstants.RIGHT;

    private final JPanel north = new JPanel();
    private final JPanel center = new JPanel();
    private final JLabel prjDir = new JLabel("项目路径：", LABEL_ALIGN);
    private final JTextField prjDirContent = new JTextField();
    private final JLabel artifactIdName = new JLabel("artifact-id：", LABEL_ALIGN);
    private final JTextField artifactIdContent = new JTextField();
    private final JLabel groupIdName = new JLabel("group-id：", LABEL_ALIGN);
    private final JTextField groupIdContent = new JTextField();

    private final JLabel authorName = new JLabel("author：", LABEL_ALIGN);
    private final JTextField authorContent = new JTextField("licode");

    private final JLabel dbHost = new JLabel("主机：", LABEL_ALIGN);
    private final JTextField dbHostContent = new JTextField();
    private final JLabel dbPort = new JLabel("端口：", LABEL_ALIGN);
    private final JTextField dbPortContent = new JTextField();
    private final JLabel dbName = new JLabel("数据库：", LABEL_ALIGN);
    private final JTextField dbNameContent = new JTextField();

    private final JLabel dbUser = new JLabel("用户名：", LABEL_ALIGN);
    private final JTextField dbUserContent = new JTextField();
    private final JLabel dbPwd = new JLabel("密码：", LABEL_ALIGN);
    private final JTextField dbPwdContent = new JTextField();
    private final JLabel dbUrl = new JLabel("连接地址：", LABEL_ALIGN);
    private final JTextField dbUrlContent = new JTextField();

    private final JLabel dbDriverName = new JLabel("数据库驱动：", LABEL_ALIGN);
    private final JTextField dbDriverContent = new JTextField();
    private JCheckBoxList list;
    private DefaultListModel<javax.swing.JCheckBox> model = new DefaultListModel<>();
    private final SpinnerNumberModel spModel = new SpinnerNumberModel();
    private final JSpinner tablePrefixContent = new JSpinner(spModel);
    private final JLabel tablePrefixName = new JLabel("表前缀：", 4);

    private final JLabel createTypeLabel = new JLabel("目录结构：", 4);
    private final JCheckBox buildDir = new JCheckBox("构建完整目录");
    private final ComboBox<CreateModal> createTypeContent = new ComboBox<>();

    private final JLabel jdkVersionLabel = new JLabel("JDK版本：", 4);
    private final ComboBox<Integer> jdkVersionContent = new ComboBox<>();

    private String createType = CodeKitConf.BM_DEFAULT;

    private ProjectContext prjContext;

    private String addPrjDir;
    private final UIBuilderUtil uiBuilder = new UIBuilderUtil(82);

    public GenerateCodeUiBuilder() {
    }

    private String getBasePath() {
        return addPrjDir;
    }

    public JPanel initNorth() {
        JLabel title = new JLabel("小码咨询工程助手");
        title.setFont(new Font("微软雅黑", 0, 16));
        title.setHorizontalAlignment(2);
        title.setVerticalAlignment(0);
        // this.north.add(null);
        return this.north;
    }

    private JComponent initTablesView() {
        list = new JCheckBoxList(model);
        list.setAutoscrolls(true);
        list.setBorder(new LineBorder(Gray._80, 1));
        JBScrollPane sPane = new JBScrollPane(list);

        sPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        sPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        sPane.setOpaque(false);
        sPane.getViewport().setOpaque(false);

        sPane.setSize(420, 180);
        sPane.getViewport().setSize(420, 180);
        sPane.setBounds(0, 0, 420, 180);

        return sPane;
    }

    private void refreshTables() {
        model.clear();
        if (!prjContext.getAssembledDb().getTables().isEmpty()) {
            for (Table item: prjContext.getAssembledDb().getTables()) {
                model.addElement(new JCheckBox(item.getName()));
            }
        }

        if (null != list) {
             list.updateUI();
        }
    }

    private void intiProjectContext(String dir) {
        String filePath = dir + "\\pom.xml";
        prjContext = new ProjectContext(CodeKitConf.CODE_TYPE);
        prjContext.parse(filePath);
        prjContext.setRootDir(dir);
        prjContext.setCurrentDir(dir);
    }

    public JComponent initCenter(Project rootPrj, String addPrjDir, DialogWrapper dialogWrapper) {
        int defWidth = 240;
        JButton refreshBtn = this.buildButton("刷新");
        JLabel dbTip = new JLabel("确保数据库驱动存在！");
        JPanel centerRow = new JPanel();
        JPanel rightPanel = new JPanel(new BorderLayout());
        GridLayout layGrid = new GridLayout(0, 1);

        centerRow.setLayout(layGrid);
        layGrid.setHgap(16);

        intiProjectContext(addPrjDir);

        this.center.setLayout(new BorderLayout());
        this.addPrjDir = addPrjDir;

        this.dbPortContent.setMinimumSize(new Dimension(80, 0));

        centerRow.add(this.buildRow(this.prjDir, this.prjDirContent, defWidth));

        // JDK版本号
        centerRow.add(this.buildRow(this.jdkVersionLabel, this.jdkVersionContent, defWidth));

        centerRow.add(this.buildRow(this.groupIdName, this.groupIdContent, defWidth));
        centerRow.add(this.buildRow(this.artifactIdName, this.artifactIdContent, defWidth));

        this.dbDriverContent.setMinimumSize(new Dimension(250, 0));

        this.dbUrlContent.setPreferredSize(new Dimension(DEFAULT_WID, 0));
        this.prjDirContent.setPreferredSize(new Dimension(DEFAULT_WID, 0));
        this.dbDriverContent.setPreferredSize(new Dimension(DEFAULT_WID, 0));

        centerRow.add(this.buildRow(this.authorName, this.authorContent, defWidth));
        centerRow.add(uiBuilder.buildRow(this.dbHost, this.dbHostContent, this.dbPort, this.dbPortContent));
        centerRow.add(this.buildRow(this.dbName, this.dbNameContent, defWidth));
        centerRow.add(uiBuilder.buildRow(this.dbUser, this.dbUserContent, this.dbPwd, this.dbPwdContent));
        centerRow.add(this.buildRow(this.dbUrl, this.dbUrlContent, defWidth));

        JButton dirBtn = this.buildButton("打开");

        centerRow.add(uiBuilder.buildRow(this.dbDriverName, this.dbDriverContent, dirBtn));

        dbTip.setForeground(JBColor.RED);

        // centerRow.add(initTablesView());
        centerRow.add(this.initBottom(rootPrj, dialogWrapper));

        rightPanel.add(this.buildRowWithLeftWid(0, refreshBtn,
                        this.buildRowWithLeftWid(0, this.tablePrefixName, this.tablePrefixContent)),
                BorderLayout.NORTH);
        rightPanel.add(initTablesView(), BorderLayout.CENTER);

        this.center.add(centerRow, BorderLayout.CENTER);
        this.center.add(rightPanel, BorderLayout.EAST);

        setInitValues();

        dirBtn.addActionListener((e) -> {
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            int option = fileChooser.showOpenDialog(this.center);
            if (option == 0) {
                File file = fileChooser.getSelectedFile();
                this.dbDriverContent.setText(file.getAbsolutePath());
            }
        });

        refreshBtn.addActionListener((e) -> {
            refreshTablesFromDb(rootPrj);
        });

        addDbUrlChangeListener();
        doChangeDbUrl();

        initModuleInfo(rootPrj);

        handleRefreshTables();
        refreshTables();

        return this.center;
    }

    private void setInitValues() {
        authorContent.setText(getConfig().getAuthor());

        dbNameContent.setText(getConfig().getDbName());
        dbHostContent.setText(getConfig().getDbHost());
        dbPortContent.setText(getConfig().getDbPort());

        dbUserContent.setText(getConfig().getDbUser());
        dbPwdContent.setText(getConfig().getDbPwd());
        dbUrlContent.setText(getConfig().getDbUri());
        dbDriverContent.setText(LiSysConfig.getInstance().getDbDriver());

        this.spModel.setValue(getConfig().getTablePrefix());

        this.dbUrlContent.setEditable(false);
        this.dbDriverContent.setEditable(false);
    }

    private void addDbUrlChangeListener() {
        this.dbHostContent.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }
        });

        this.dbPortContent.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }
        });

        this.dbNameContent.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                doChangeDbUrl();
            }
        });
    }

    private void initModuleInfo(Project rootPrj) {
        String moduleType = prjContext.getConfig().getModuleType();

        createTypeContent.setEditable(false);
        createTypeContent.addItem(new CreateModal("默认", CodeKitConf.BM_DEFAULT));
        createTypeContent.addItem(new CreateModal("领域架构", CodeKitConf.BM_DOMAIN));

        jdkVersionContent.setEditable(false);
        jdkVersionContent.addItem(8);
        jdkVersionContent.addItem(17);
        jdkVersionContent.addItem(21);

        buildDir.setVisible(false);
        prjContext.setBuildDirWhenGenCode(false);

        if (CodeKitConf.MT_SINGLE_SIX.equals(moduleType) ||
                CodeKitConf.MT_SPEC_SIX.equals(moduleType)) {
            createTypeContent.setSelectedIndex(1);
            createType = CodeKitConf.BM_DOMAIN;
            createTypeContent.setEnabled(false);
        } else if (CodeKitConf.MT_SIMPLE_ARCH.equals(moduleType)) {
            createTypeContent.setSelectedIndex(0);
            createTypeContent.setEnabled(false);
        } else {
            createTypeContent.setSelectedIndex(0);
            createTypeContent.setEnabled(true);
            buildDir.setVisible(true);
            prjContext.setBuildDirWhenGenCode(buildDir.isSelected());
        }

        if (17 == prjContext.getJdkVersion()) {
            jdkVersionContent.setSelectedIndex(1);
        } else if (21 == prjContext.getJdkVersion()) {
            jdkVersionContent.setSelectedIndex(2);
        } else {
            jdkVersionContent.setSelectedIndex(0);
        }

        buildDir.addChangeListener(e -> prjContext.setBuildDirWhenGenCode(buildDir.isSelected()));

        createTypeContent.addItemListener(e -> {
            if (e.getItem() instanceof CreateModal) {
                createType = ((CreateModal)e.getItem()).getType();
            }
        });

        jdkVersionContent.addItemListener(e -> {
            if (e.getItem() instanceof Integer) {
                this.prjContext.setJdkVersion((Integer)e.getItem());
            }
        });

        this.prjDirContent.setText(prjContext.getRootDir());
        this.groupIdContent.setText(prjContext.getGroupId());
        this.artifactIdContent.setText(prjContext.getArtifactId());

        this.prjDirContent.setEditable(false);
        this.groupIdContent.setEditable(false);
        this.artifactIdContent.setEditable(false);
    }

    private JComponent buildRowWithLeftWid(int lWidth, Component left, Component right) {
        left.setMinimumSize(new Dimension(lWidth, 0));
        return uiBuilder.buildFillRow(lWidth, left, right);
    }

    private JComponent buildRow(Component left, Component right, int rWidth) {
        right.setMinimumSize(new Dimension(rWidth, 0));
        right.setMaximumSize(new Dimension(rWidth, 0));
        return uiBuilder.buildFillRow(left, right);
    }

    private JButton buildButton(String text) {
        JButton submit = new JButton(text);
        submit.setHorizontalAlignment(0);
        submit.setVerticalAlignment(0);
        return submit;
    }

    public JComponent initBottom(Project rootPrj, DialogWrapper dialogWrapper) {
        JButton buildCode = this.buildButton("生成代码");
        JButton buildEntity = this.buildButton("生成实体");
        JButton cancel = this.buildButton("退出");
        FlowLayout fl = new FlowLayout();
        JPanel panel = new JPanel(fl);
        fl.setAlignment(2);

        panel.add(createTypeLabel);
        panel.add(createTypeContent);
        panel.add(buildDir);

        panel.add(buildCode);
        panel.add(buildEntity);
        panel.add(cancel);

        buildCode.addActionListener((e) -> {
            this.doCreateCode(dialogWrapper, rootPrj, false);
        });
        buildEntity.addActionListener((e) -> {
            this.doCreateCode(dialogWrapper, rootPrj, true);
        });
        cancel.addActionListener((e) -> {
            dialogWrapper.close(0);
        });
        return panel;
    }

    private LiConfig getConfig() {
        return this.prjContext.getConfig();
    }

    private PomFile getPom() {
        return this.prjContext.getPomFile();
    }

    private void doChangeDbUrl() {
        String dbFmt = getConfig().getDbUrlTemplate();
        String host = this.dbHostContent.getText();
        String port = this.dbPortContent.getText();
        String name = this.dbNameContent.getText();

        this.dbUrlContent.setText(dbFmt.replace("{host}", host)
                .replace("{port}", port)
                .replace("{name}", name));
    }

    private void refreshTablesFromDb(Project project) {
        String dbUser = this.dbUserContent.getText();
        String dbPwd = this.dbPwdContent.getText();
        String dbUri = this.dbUrlContent.getText();

        if (StringUtil.isEmpty(dbUser)) {
            Messages.showMessageDialog(project, "请输入数据库用户名",
                    "温馨提示", Messages.getInformationIcon());
        } else if (StringUtil.isEmpty(dbPwd)) {
            Messages.showMessageDialog(project, "请输入数据库密码",
                    "温馨提示", Messages.getInformationIcon());
        } else if (StringUtil.isEmpty(dbUri)) {
            Messages.showMessageDialog(project, "请输入数据库连接地址",
                    "温馨提示", Messages.getInformationIcon());
        } else {
            if (!handleRefreshTables()) {
                if (AssembledDb.dbDriverValid()) {
                    Messages.showMessageDialog(project, "打开数据库失败，请检查数据库用户或密码是否正确！",
                            "温馨提示", Messages.getInformationIcon());
                } else {
                    String dbDriverPath = LiSysConfig.getInstance().getDbDriver();
                    if (DbDriverDlUtil.downMysqlFromResource(dbDriverPath, true)) {
                        this.dbDriverContent.setText(dbDriverPath);
                        // 重新初始化数据库
                        AssembledDb.initDbClassloader(dbDriverPath, false);
                        if (!AssembledDb.dbDriverValid()) {
                            Messages.showMessageDialog(project, "初始化失败，数据库驱动无效或不可用！",
                                    "温馨提示", Messages.getInformationIcon());
                        } else if (!handleRefreshTables()) {
                            Messages.showMessageDialog(project, "打开数据库失败，请检查数据库用户或密码是否正确！",
                                    "温馨提示", Messages.getInformationIcon());
                        }
                    } else {
                        Messages.showMessageDialog(project, "数据库驱动缺失，请重新选择！",
                                "温馨提示", Messages.getInformationIcon());
                    }
                }
            }
        }
    }

    private boolean handleRefreshTables() {
        String dbUser = this.dbUserContent.getText();
        String dbPwd = this.dbPwdContent.getText();
        String dbUri = this.dbUrlContent.getText();
        String moduleName = this.getPom().getModuleName("");
        String author = this.authorContent.getText();
        int tabPrefix = this.spModel.getNumber().intValue();
        String host = this.dbHostContent.getText();
        String port = this.dbPortContent.getText();
        String name = this.dbNameContent.getText();

        if (!StringUtil.isEmpty(dbUser) && !StringUtil.isEmpty(dbPwd) &&
                !StringUtil.isEmpty(dbPwd) && !StringUtil.isEmpty(dbUri)) {
            prjContext.setDbUser(dbUser);
            prjContext.setDbPwd(dbPwd);
            prjContext.setDbUrl(dbUri);
            prjContext.setTablePrefix(tabPrefix);
            prjContext.setModuleName(moduleName);

            getConfig().setDbName(name);
            getConfig().setDbHost(host);
            getConfig().setDbPort(port);
            getConfig().setDbUser(dbUser);
            getConfig().setDbPwd(dbPwd);
            getConfig().setDbUri(dbUri);
            getConfig().setAuthor(author);
            getConfig().setTablePrefix(tabPrefix);

            getConfig().save();

            if (!org.jsoup.internal.StringUtil.isBlank(name) &&
                    prjContext.getAssembledDb().initTables(dbUri, dbUser, dbPwd)) {
                refreshTables();
                return true;
            }
        }

        return false;
    }

    private void doCreateCode(DialogWrapper dialogWrapper, Project project, boolean onlyEntity) {
        if (!prjContext.isCanGenerateCode()) {
            Messages.showMessageDialog(project, "此模块不能自动生成代码！",
                    "温馨提示", Messages.getInformationIcon());
        } else {
            java.util.List<Table> tables = getSelectedTable();
            if (tables.isEmpty()) {
                Messages.showMessageDialog(project, "请选择数据库表名！",
                        "温馨提示", Messages.getInformationIcon());
            } else {
                this.generateCode(dialogWrapper, project, tables, onlyEntity);
            }
        }
    }

    private java.util.List<Table> getSelectedTable() {
        java.util.List<Table> tabs = new ArrayList<>();

        if (null != list) {
            ListModel<javax.swing.JCheckBox> listModel = list.getModel();
            if (null != listModel) {
                for (int i = 0; i < listModel.getSize(); ++i) {
                    JCheckBox jcb = listModel.getElementAt(i);
                    if (jcb.isSelected()) {
                        tabs.add(prjContext.getAssembledDb().getTables().get(i));
                    }
                }
            }
        }

        return tabs;
    }

    private void generateCode(DialogWrapper dialogWrapper, Project rootPrj,
                              java.util.List<Table> tabs, boolean onlyEntity) {
        CodeGenerateBuilder codeBuilder = new CodeGenerateBuilder();
        int tabPrefix = spModel.getNumber().intValue();
        getConfig().setTablePrefix(Math.max(tabPrefix, 0));
        getConfig().save();

        prjContext.setTables(tabs);
        prjContext.setOnlyEntity(onlyEntity);

        if (codeBuilder.buildEntityCode(rootPrj, prjContext, this.createType)) {
            Messages.showMessageDialog(rootPrj, "代码生成完成！",
                    "温馨提示", Messages.getInformationIcon());
            dialogWrapper.close(0);
        }
    }
}
